/*
* Copyright (c) 2011 Connection Road.  All rights reserved.
* This program and the accompanying material are made available
* under the terms of the Eclipse Public Licnese v1.0 which is
* available at http://www.eclipse.org/legal/epl-v10.html
*/

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using CrSyncFilesUtils.Enums;
using CrSyncFilesUtils.Properties;

namespace CrSyncFilesUtils
{
    /// <summary>
    /// A collection of static methods that uses standard I/O for file sychronization built specifically for command line processes.
    /// </summary>
    public class CrSyncFilesConsoleUtility
    {

        public string LogFilePath { get; set; }
        public string CriticalLogFilePath { get; set; }
        public string MetaDataDirectory { get; set; }
        public string Prefix { get; set; }
        public FileSyncPairManager PairManager { get; set; }
        public CrSyncToyManager SyncToyManager { get; set; }

        private bool _previewMode;
        


        public CrSyncFilesConsoleUtility(string metaDataDirectory , string prefix, FileSyncPairManager pairManager, CrSyncToyManager syncToyManager, string logFilePath, string criticalLogFilePath)
        {
            MetaDataDirectory = metaDataDirectory;
            Prefix = prefix;
            PairManager = pairManager;
            SyncToyManager = syncToyManager;
            LogFilePath = logFilePath;
            CriticalLogFilePath = criticalLogFilePath;
        }

        public CrSyncFilesConsoleUtility(FileSyncPairManager pairManager, CrSyncToyManager syncToyManager)
        {
            PairManager = pairManager;
            SyncToyManager = syncToyManager;
        }


        /// <summary>
        /// Lists all available folder pairs from the console.
        /// </summary>
        public void PrintAvailableFolderPairs()
        {
            var availableFolderPairs = PairManager.AvailableFolderPairs();
            
            Console.WriteLine("\n-----------------------------------------------");
            Console.WriteLine(Resources.TEXT_CONSOLE_ABOUT+"\n\n");

            
            foreach (var pair in availableFolderPairs.Select(availableFolderPair => availableFolderPair.Value))
            {
                Console.WriteLine(Resources.TEXT_FOLDER_PAIR_INFO, pair.FolderPairName, pair.LeftFolder.Path, pair.RightFolder.Path, pair.LastRunDisplayString);
            }

            Console.WriteLine("\n\n"+Resources.TEXT_TOTAL_FOLDER_PAIRS, availableFolderPairs.Count);
        }


        public void CreateFolderPair(string pairName,
                                     string leftFolder,
                                     string rightFolder,
                                     FolderPairActionTypeEnum actionType,
                                     string includeFiles,
                                     string excludeFiles,
                                     bool checkFileContents)
        {
            var newFolderPair = FileSyncPairManager.CreateFolderPair(pairName);
            newFolderPair.LeftFolder = FileSyncPairManager.CreateFileLocation(leftFolder, Prefix, Guid.NewGuid().ToString(), MetaDataDirectory);
            newFolderPair.RightFolder = FileSyncPairManager.CreateFileLocation(rightFolder, Prefix, Guid.NewGuid().ToString(), MetaDataDirectory);
            newFolderPair.FolderPairActionTypeId = (int) actionType;
            newFolderPair.SetSearchPattern(includeFiles);
            newFolderPair.SetExcludePattern(excludeFiles);
            newFolderPair.OptionsCompareFileStreams = checkFileContents;
            newFolderPair.ActiveForRunAll = true;

            var result =  PairManager.AddFolderPair(newFolderPair);
            switch (result)
            {
                case CrSyncFilesResult.SaveSuccessful:
                    if (SyncToyManager != null) SyncToyManager.AddFolderPair(newFolderPair); // Import to synctoy all newly added folder pair
                    Console.WriteLine(Resources.TEXT_ADDED_SUCCESSFULLY, pairName);
                    break;
                case CrSyncFilesResult.Found:
                    Console.WriteLine(Resources.TEXT_FOLDER_PAIR_ALREADY_EXISTS);
                    break;
                case CrSyncFilesResult.SaveFailed:
                    Console.WriteLine(Resources.TEXT_PROBLEM_ADDING_PAIR,pairName);
                    break;
                default:
                    Console.WriteLine(Resources.TEXT_PROBLEM_ADDING_PAIR, pairName);
                    break;
            }
        }

        public void DeleteFolderPairNamed(string pairName)
        {
            if(PairManager.HasFolderPairNamed(pairName))
            {
                if (PairManager.RemoveFolderPairNamed(pairName))
                {
                    if (SyncToyManager != null) SyncToyManager.RemoveFolderPairNamed(pairName); // Also delete all folder pairs in synctoy
                    Console.WriteLine(Resources.TEXT_PAIR_REMOVED, pairName);
                }
            }
            else
            {
                Console.WriteLine(Resources.TEXT_NO_FOLDER_PAIR_ASSOCIATED);
                Console.WriteLine(Resources.TEXT_FORGET_FOLDER_PAIR);
            }
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pairName">Name of the folder pair to run.</param>
        /// <param name="previewMode">If set to true, crsynfiles will run the pair in preview mode</param>
        /// <param name="settingsBase"></param>
        /// <param name="provider"></param>
        public void RunFolderPairNamed(string pairName, bool previewMode, SettingsBase settingsBase ,string provider)
        {
            if(PairManager.HasFolderPairNamed(pairName))
            {
                RunFolderPairs(new List<FileSyncPairInfo>{PairManager.CurrentFolderPair},  previewMode, settingsBase, provider );
            }
            else
            {
                Console.WriteLine(Resources.TEXT_NO_FOLDER_PAIR_ASSOCIATED);
            }
        }

        public void RunAllFolderPairs(bool previewMode, SettingsBase settingsBase,string provider)
        {
            var availableFolderPairs = PairManager.AvailableFolderPairs();
            var quedPairs = availableFolderPairs.Select(pkv => pkv.Value).ToList();
            
            RunFolderPairs(quedPairs,previewMode, settingsBase, provider);
        }
        
        public void RunFolderPairs(List<FileSyncPairInfo>availableFolderPairs, bool previewMode, SettingsBase settingsBase,string provider)
        {

            // Loops through all folder pairs  and do stuffs before doing the sync.
            foreach (var fileSyncPairInfo in availableFolderPairs)
            {

                // Create marker files.
                if (fileSyncPairInfo.LeftFolder.Validate(fileSyncPairInfo, PairManager) == CrSyncFileLocationResult.Valid)
                {
                    fileSyncPairInfo.LeftFolder.CreateMarker();
                }

                if (fileSyncPairInfo.RightFolder.Validate(fileSyncPairInfo, PairManager) == CrSyncFileLocationResult.Valid)
                {
                    fileSyncPairInfo.RightFolder.CreateMarker();
                }

                // Update the last run date.
                fileSyncPairInfo.DateTimeLastSync = DateTime.Now;
                SyncToyManager.UpdateFolderPair(fileSyncPairInfo);
                PairManager.UpdateFolderPair(fileSyncPairInfo);
            }

            PairManager.UpdateFolderPairs(availableFolderPairs);

            var synchronizerManager = new FileSynchronizerManager(availableFolderPairs, previewMode, settingsBase, provider);
            var fileSynchronizer = synchronizerManager.GetSynchronizer();
            _previewMode = previewMode;

            // Register the event handlers
            fileSynchronizer.FilesSyncingCompleted += FileSynchronizerFilesSyncingCompleted;
            fileSynchronizer.FileSyncStarted += FileSynchronizerFileSyncStarted;
            fileSynchronizer.FilesSyncing += FileSynchronizerFilesSyncing;
            fileSynchronizer.PreviewingSync += FileSynchronizerSyncingPreview;
            fileSynchronizer.LogMessages += FileSynchronizerLogMessages;

            // This is a synchronous call.  it will not return until done.
            fileSynchronizer.Synchronize(250, 100);

            Console.WriteLine(Resources.TEXT_DONE_RUNNING_FOLDER_PAIRS,availableFolderPairs.Count);
        }

        public void FileSynchronizerLogMessages(object sender, FileSyncMessageEventArgs args)
        {
            if(!string.IsNullOrEmpty(LogFilePath))
            {
                if(!string.IsNullOrEmpty(args.Message))
                {
                    CrSyncFilesUtility.WriteLog(args.Message,LogFilePath);
                }
            }
        }

        public void FileSynchronizerSyncingPreview(object sender, FileSyncEventArgs e)
        {
            if(_previewMode)
            {
                Console.WriteLine(Resources.TEXT_PREVIEW_MODE + e.Message);
            }
            else
            {
                Console.WriteLine(Resources.TEXT_FILE_SYNCING + e.Message);
            }
        }

        public void FileSynchronizerFilesSyncing(object sender, FileSyncEventArgs e)
        {
            Console.WriteLine(Resources.TEXT_FILE_SYNCING + e.Message);
        }

        public void FileSynchronizerFileSyncStarted(object sender, FileSyncEventArgs e)
        {
            Console.WriteLine(Resources.TEXT_FILE_SYNC_STARTED);
        }

        public void FileSynchronizerFilesSyncingCompleted(object sender, FileSyncEventArgs e)
        {
            Console.WriteLine(Resources.TEXT_FILE_SYNC_FINISHED);
        }

        public void RenameFolderPair(string oldName, string newName)
        {
            var cleanedOldName = oldName.Trim();
            var cleanedNewName = newName.Trim();

            if(oldName != newName)
            {
                
                if(PairManager.RenameFolderPair(cleanedOldName, cleanedNewName))
                {
                    SyncToyManager.RenameFolderPair(cleanedOldName, cleanedNewName);
                    Console.WriteLine("Successfully renamed folder pair.");
                }
                else
                {
                    Console.WriteLine("Failed to rename folder pair");
                }

            }

            PrintAvailableFolderPairs();
        }

        public void Parse(string[] consoleArguments, SettingsBase settingsBase, string provider)
        {
            var cmdParser = new CrSyncFileCmdParser(consoleArguments);
            if (cmdParser.ContainsParameters(new[] { "-?", "-h", "--help" }, false))
            {
                // Always parse help command first to prevent undesired action.
                PrintHelp();
            }
            else if (cmdParser.ContainsParameters(new[] { "-c", "--create" }, true))
            {
                // Create folder management

                var pairName = cmdParser.CurrentArgument;
                if (cmdParser.ContainsParameters(new[] { "--left" }, true))
                {
                    var leftFolder = cmdParser.CurrentArgument;

                    if (cmdParser.ContainsParameters(new[] { "--right" }, true))
                    {
                        var rightFolder = cmdParser.CurrentArgument;
                        var actionType = FolderPairActionTypeEnum.Synchronize;
                        if (cmdParser.ContainsParameters(new[] { "--synchronize","--sync","--synchro","-s" }, false))
                        {
                            actionType = FolderPairActionTypeEnum.Synchronize;
                        }
                        else if (cmdParser.ContainsParameters(new[] { "--echo","-e" }, false))
                        {
                            actionType = FolderPairActionTypeEnum.Echo;
                        }
                        else if (cmdParser.ContainsParameters(new[] { "--contribute","--contrib" }, false))
                        {
                            actionType = FolderPairActionTypeEnum.Contribute;
                        }

                        string include = null;
                        string exclude = null;
                        var checkFileContents = false;

                        if (cmdParser.ContainsParameters(new[] { "--include" }, true))
                        {
                            include = cmdParser.CurrentArgument;
                        }

                        if (cmdParser.ContainsParameters(new[] { "--exclude" }, true))
                        {
                            exclude = cmdParser.CurrentArgument;
                        }

                        if (cmdParser.ContainsParameters(new[] { "--check", "--check-file-contents" }, false))
                        {
                            checkFileContents = true;
                        }

                        CreateFolderPair(pairName,leftFolder, rightFolder, actionType, include, exclude, checkFileContents);

                    }
                }


            }
            else if (cmdParser.ContainsParameters(new[] { "-r", "--run" }, true))
            {

                // Run single pair

                var pairName = cmdParser.CurrentArgument;
                var preview = false;
                if (cmdParser.ContainsParameters(new[] { "-p", "--preview" }, false))
                {
                    preview = true;
                }
                RunFolderPairNamed(pairName, preview, settingsBase, provider);
            }
            else if (cmdParser.ContainsParameters(new[] { "-R", "--Run", "--Run-all","--Run-All" }, false))
            {
                // Run available folder pair
                var preview = false;
                if (cmdParser.ContainsParameters(new[] { "-p", "--preview" }, false))
                {
                    preview = true;
                }
                RunAllFolderPairs(preview, settingsBase, provider);
            }
            else if (cmdParser.ContainsParameters(new[] { "-d", "--delete" }, true))
            {
                var pairName = cmdParser.CurrentArgument;
                DeleteFolderPairNamed(pairName);
            }
            else if(cmdParser.ContainsParameters(new[]{"--rename","--ren","-n"},true))
            {
                var oldName = cmdParser.CurrentArgument;
                var newName = cmdParser.Next(null);
                RenameFolderPair(oldName,newName);
            }
            else if (cmdParser.ContainsParameters(new[] { "-l","--list" }, false))
            {
                PrintAvailableFolderPairs();
            }
            else
            {
                PrintHelp();
            }
            
        }

        public static void PrintHelp()
        {
            Console.WriteLine(Resources.FILE_CONSOL_HELP);
        }
    }
}
